﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using ModbusLib.Base;
using ProtocalLib.Base;

namespace ModbusLib
{
    public class ModbusTCP : ModbusSocket
    {
        public ModbusTCP() : this("127.0.0.1")
        {

        }
        public ModbusTCP(string Ip,int port = 502) : this(Ip,port,5000)
        {
        }

        public ModbusTCP(string Ip, int port,int recvTimeout) : base(ProtocolType.Tcp)
        {
            IP = Ip;
            Port = port;
            ReadTimeout = recvTimeout;
        }


        int _tid = 0;
        private static readonly object _lockObj = new object();
        private int CreateTID()
        {
            lock (_lockObj)
            {
                _tid++;
                _tid = _tid % 65525;
                return _tid;
            }
        }

        public override byte[] Read(byte slave, FunctionType func, ushort start, ushort count,Action<object> callback = null)
        {
            int tid = CreateTID();
            List<byte> reqBytes = new List<byte>()
            {
                (byte)(tid / 256),
                (byte)(tid % 256),
                0x00,0x00
            };
            byte[] datas = GetReadBytes(slave,(byte)func,start,count);
            reqBytes.Add((byte)(datas.Length / 256));
            reqBytes.Add((byte)(datas.Length % 256));
            reqBytes.AddRange(datas);
            callback?.Invoke(new LogMessage { MsgType = "Send", Data = reqBytes.ToArray() });

            List<byte> resp = this.SendAndReceive(reqBytes.ToArray(), -1).ToList();

            if (resp[1] > 0x80)
            {
                // record log
                if (callback != null)
                {
                    LogMessage logMessage = new LogMessage { MsgType = "Recv", Data = resp.ToArray() };
                    if (resp[1] > 0x80)
                    {
                        logMessage.ErrorMsg = ModbusBase.Errors[resp[2]];
                    }
                    callback.Invoke(logMessage);
                }
                throw new Exception(ModbusBase.Errors[resp[2]]);
            }

            return resp.GetRange(3, resp.Count - 3).ToArray();
        }

        public override void ReadAsync(byte slave, FunctionType func, ushort start, ushort count, int handler, Action<byte[], int, Exception> callback)
        {
            int tid = CreateTID();
            List<byte> reqBytes = new List<byte>()
            {
                (byte)(tid / 256),
                (byte)(tid % 256),
                0x00,0x00
            };
            byte[] datas = GetReadBytes(slave, (byte)func, start, count);
            reqBytes.Add((byte)(datas.Length / 256));
            reqBytes.Add((byte)(datas.Length % 256));
            reqBytes.AddRange(datas);

            this.AsyncModels.Insert(0, new AsyncModel()
            {
                Handler = handler,
                ReqBytes = reqBytes.ToArray(),
                RespLen = -1,
                Completed = callback
            });
        }

        public override void Write(byte slave, FunctionType func, ushort start, ushort count, byte[] datas,Action<object> callback = null)
        {
            int tid = CreateTID();
            List<byte> reqBytes = new List<byte>()
            {
                (byte)(tid / 256),
                (byte)(tid % 256),
                0x00,0x00
            };

            byte[] reqBody = GetWriteBytes(slave,(byte)func,start,count,datas);
            reqBytes.Add((byte)(reqBody.Length / 256));
            reqBytes.Add((byte)(reqBody.Length % 256));
            reqBytes.AddRange(datas);
            callback?.Invoke(new LogMessage { MsgType = "Send", Data = reqBytes.ToArray() });

            byte[] resp = this.SendAndReceive(reqBytes.ToArray(), -1);
            if (resp[1] > 0x80)
            {
                // record log
                if (callback != null)
                {
                    LogMessage logMessage = new LogMessage { MsgType = "Recv", Data = resp.ToArray() };
                    if (resp[1] > 0x80)
                    {
                        logMessage.ErrorMsg = ModbusBase.Errors[resp[2]];
                    }
                    callback.Invoke(logMessage);
                }
                throw new Exception(ModbusBase.Errors[resp[2]]);
            }
        }

        public override void WriteAsync(byte slave, FunctionType func, ushort start, ushort count, byte[] datas, int handler, Action<byte[], int, Exception> callback)
        {
            int tid = CreateTID();
            List<byte> reqBytes = new List<byte>()
            {
                (byte)(tid / 256),
                (byte)(tid % 256),
                0x00,0x00
            };

            byte[] reqBody = GetWriteBytes(slave, (byte)func, start, count, datas);
            reqBytes.Add((byte)(reqBody.Length / 256));
            reqBytes.Add((byte)(reqBody.Length % 256));
            reqBytes.AddRange(datas);

            this.AsyncModels.Insert(0, new AsyncModel()
            {
                Handler = handler,
                ReqBytes = reqBytes.ToArray(),
                RespLen = -1,
                Completed = callback
            });
        }
    }
}
